#include "stdafx.h"
#include "FileImpl.h"

#include <locale>
#include <iostream>
#include <sstream>

#include "boost/filesystem.hpp"
#include "boost/lexical_cast.hpp"
namespace bfs = boost::filesystem;

#ifdef _DEBUG
#define new DEBUG_NEW
#endif


namespace reckon {

	namespace util {

		TextFile	createTextFile(LPCTSTR lpszPath)
		{
			return TextFile(new TextFileImpl(lpszPath));
		}

	}

	TextFileImpl::TextFileImpl(LPCTSTR lpszPath, IFile::open_mode mode, IFile::file_encoding encoding)
		: _filePath(lpszPath)
		, _mode(mode)
		, _encoding(encoding)
		, _fp(nullptr)
		, _bOpened(false)
		//, _buf(lpszPath)
	{
	}
	TextFileImpl::TextFileImpl(LPCTSTR lpszPath)
		: _filePath(lpszPath)
		, _mode(IFile::mode_undefined)
		, _encoding(IFile::ansi)
		, _fp(nullptr)
		, _bOpened(false)
	{
	}

	TextFileImpl::~TextFileImpl()
	{
	}

	bool TextFileImpl::close()
	{
		return false;
	}

	bool TextFileImpl::openToRead()
	{
		if (!_checkPath() || _fp != nullptr || _bOpened)
			return false;

		_mode = IFile::mode_read;
		_encoding = IFile::ansi;

		if (_open())
			return true;

		return _bOpened;
	}
	bool TextFileImpl::openToWrite(IFile::file_encoding encoding)
	{
		_mode = IFile::mode_write;
		_encoding = encoding;

		if (_open())
			return true;

		return false;
	}

	bool TextFileImpl::openToAppend()
	{
		_mode = IFile::mode_append;
		_encoding = IFile::ansi;

		if (_open())
			return true;

		return false;
	}

	IFile::file_encoding TextFileImpl::getEncoding()
	{
		return _encoding;
	}

	bool TextFileImpl::writeLine(const tstring& buf)
	{

		return false;
	}

	bool TextFileImpl::readLine(tstring& buf)
	{
		bool ret = false;

		switch (_encoding)
		{
		case IFile::ansi:
			ret = _readLineA(buf);
			break;
		//case IFile::unicode:
		//case IFile::utf8:
		//	ret = _readLineW(buf);
			break;
		}
		return ret;
	}

	FILE* TextFileImpl::_open()
	{
		tstring flag(_T(""));
		switch (_mode)
		{
		case IFile::mode_read:
			flag = _T("r");
			break;
		case IFile::mode_write:
			flag = _T("w");
			break;
		case IFile::mode_append:
			flag = _T("a+");
			break;

		case IFile::mode_undefined:
		default:
			_mode = IFile::mode_undefined;
			return nullptr;
		}

		if (_mode != IFile::mode_read)
		{
			switch (_encoding)
			{
			//case IFile::unicode:
			//	flag.append(_T(", css=UNICODE"));
			//	break;
			//case IFile::utf8:
			//	flag.append(_T(", css=UTF-8"));
			//	break;
			//case IFile::utf16le:
			//	flag.append(_T(", css=UTF-16LE"));
			//	break;
			case IFile::ansi:
				break;

			case IFile::binary:
				return nullptr;
			}
		}


		errno_t err = _tfopen_s(&_fp, _filePath.c_str(), flag.c_str());

		if (_fp)
		{
			// encoding update
			if (_mode == IFile::mode_read)
			{
				_encoding = _checkBOM();
			}
			else if (_mode == IFile::mode_append)
			{
				// override encoding 
				_encoding = _checkBOM();
			}
			else if (_mode == IFile::mode_write)
			{ // write BOM
				_writeBOM();
			}
		}
		else
		{
			return nullptr;
		}

		if (_fp != nullptr)
			_bOpened = true;
		else
			_bOpened = false;

		return _fp;
	}

	bool TextFileImpl::_writeBOM()
	{
		switch (_encoding)
		{
		case IFile::ansi:
			{
				// do nothing
			}
			break;
		default:
			return false;
		}

		return true;
	}

	IFile::file_encoding TextFileImpl::_checkBOM()
	{
		if (fseek(_fp, 0, SEEK_SET) == 0)
		{
			u8 buf;
			fread_s(&buf, 1, 1, 1, _fp);

			switch (buf)
			{
			//case 0xFF: // FF FE >> UTF-16LE
			//	{
			//		u8 buf2;
			//		fread_s(&buf2, 1, 1, 1, _fp);

			//		if (buf2 == 0xFE)
			//			_encoding = IFile::unicode;
			//	}
			//	break;

			//case 0xEF: // EF BB BF >> UTF-8
			//	{
			//		u8 buf2;
			//		fread_s(&buf2, 1, 1, 1, _fp);

			//		if (buf2 == 0xBB)
			//		{
			//			u8 buf3;
			//			fread_s(&buf3, 1, 1, 1, _fp);
			//			if (buf3 == 0xBF)
			//				_encoding = IFile::utf8;
			//		}
			//		
			//	}
			//	break;

			//case 0xFE: // FE FF >> UTF-16BE
			//	{
			//		u8 buf2;
			//		fread_s(&buf2, 1, 1, 1, _fp);


			//	}
			//	break;
			default:
				fseek(_fp, 0, SEEK_SET);
				_encoding = IFile::ansi;
				break;
			}
		}
		else
		{
		}

		return _encoding;
	}

	bool TextFileImpl::_checkPath()
	{
		bool ret = false;

		bfs::path p(_filePath);

		if (bfs::exists(p))
			if (/*bfs::is_symlink(p) || */bfs::is_directory(p)) // invalid target
				ret = false;
			if (bfs::is_regular_file(p))
				ret = true;
		else	// target does not exist
			ret = false;

		return ret;
	}

	bool TextFileImpl::_readBytes(void* buff, u32 byteSize)
	{
		if (_fp == nullptr || !_bOpened)
			return false;

		size_t readCnt = fread(buff, byteSize, 1, _fp);

		return readCnt == 1;
	}

	bool TextFileImpl::_readLineW(tstring& buf)
	{
		bool ret = false;
		bool flag = false;
		wchar_t ch;

		buf = _T("");

		ret = _readBytes(&ch, sizeof(ch));

		while (ret)
		{
			if (flag && ch == L'\n') 
				break;
			else
				flag = false;

			if (ch == L'\n')
				break;

			if (ch == L'\r')
			{ 
				flag = true; 
			}
			else
			{
				buf += ch;
			}
			ret = _readBytes(&ch, sizeof(ch));
		}

		return ret;
	}

	bool TextFileImpl::_readLineA(tstring& buf)
	{
		bool ret = false;
		bool flag = false;
		char ch;

		std::string str("");

		buf = _T("");

		ret = _readBytes(&ch, sizeof(ch));

		int ml = _mbclen((unsigned char*)&ch);

		while (ret)
		{
			if (flag && ch == '\n') 
				break;
			else
				flag = false;

			if (ch == '\n')
				break;

			if (ch == '\r')
			{ 
				flag = true; 
			}
			else
			{
				str += ch;
				//buf += ch;
			}

			ret = _readBytes(&ch, sizeof(ch));
		}

		tstring ts(_T(""));
		ts.resize(str.size(), _T(''));

		std::copy(str.begin(), str.end(), ts.begin());

		buf = ts;

		int dbg = 0;
		
		return ret;
	}
}